package com.llollox.openharmonyprojects.compoundbuttongroup;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.ScrollView;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * Created by rigatol
 */

public class CompoundButtonGroup extends ScrollView {
    private static final String TAG = "CompoundButtonGroup";
    private final Context mContext;
    private final ArrayList<FullWidthCompoundButton> buttons = new ArrayList<>();
    private final FullWidthCompoundButtonListener fullWidthCompoundButtonListener
            = new FullWidthCompoundButtonListener();
    private CompoundType compoundType = CompoundType.CHECK_BOX;
    private LabelOrder labelOrder = LabelOrder.BEFORE;
    private int numCols = 1;
    private LinkedHashMap<String, String> entries;
    private OnButtonSelectedListener onButtonSelectedListener;
    private DirectionalLayout containerLayout;

    /**
     * Class Constructor
     *
     * @param context passing class context
     */
    public CompoundButtonGroup(Context context) {
        super(context);
        this.mContext = context;
        init(context);
    }

    /**
     * Class Constructor
     *
     * @param context passing class context
     * @param attrSet set of attributes needed
     */
    public CompoundButtonGroup(Context context, AttrSet attrSet) {
        super(context, attrSet);
        this.mContext = context;
        new StyledAttributes(attrSet);
        init(context);
    }

    private void init(Context context) {
        setLayoutConfig(new DirectionalLayout.LayoutConfig(
                LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT));

        ShapeElement shapeElement1 = new ShapeElement();
        shapeElement1.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));

        containerLayout = new DirectionalLayout(context);
        containerLayout.setOrientation(DirectionalLayout.VERTICAL);
        containerLayout.setBackground(shapeElement1);
        containerLayout.setLayoutConfig(new DirectionalLayout.LayoutConfig(
                ComponentContainer.LayoutConfig.MATCH_PARENT,
                ComponentContainer.LayoutConfig.MATCH_CONTENT));

        addComponent(containerLayout);
    }

    /**
     * Helps to get list of checked items
     *
     * @return checked position list
     */
    public List<Integer> getCheckedPositions() {
        ArrayList<Integer> checked = new ArrayList<>();
        for (int i = 0; i < buttons.size(); i++) {
            FullWidthCompoundButton button = buttons.get(i);
            if (button.isChecked()) {
                checked.add(i);
            }
        }
        return checked;
    }

    /**
     * Helps to set list of checked items to array
     *
     * @param checkedPositions list of checked positions
     */
    public void setCheckedPositions(List<Integer> checkedPositions) {
        for (int i = 0; i < buttons.size(); i++) {
            buttons.get(i).setChecked(checkedPositions.contains(i));
        }
    }

    /**
     * API to get type of component : checkbox or radio
     *
     * @return Component type
     */
    public CompoundType getCompoundType() {
        return compoundType;
    }

    /**
     * API to set type of component
     *
     * @param compoundType type of component to set
     */
    public void setCompoundType(CompoundType compoundType) {
        this.compoundType = compoundType;
    }

    /**
     * API to get where to keep label
     *
     * @return label order before or after
     */
    public LabelOrder getLabelOrder() {
        return labelOrder;
    }

    /**
     * API to set label order
     *
     * @param labelOrder before or after label order
     */
    public void setLabelOrder(LabelOrder labelOrder) {
        this.labelOrder = labelOrder;
    }

    /**
     * API gets the number of columns.
     *
     * @return number of cols
     */
    public int getNumCols() {
        return numCols;
    }

    /**
     * API to set num of cols
     *
     * @param numCols int value of cols
     */
    public void setNumCols(int numCols) {
        if (numCols > 0) {
            this.numCols = numCols;
        } else {
            LogUtil.error(TAG, "Cannot set a number of cols that isn't greater than zero");
        }
    }

    /**
     * API to refresh UI with new values
     */
    public void reDraw() {
        containerLayout.removeAllComponents();
        buttons.clear();

        if (numCols == 1) {
            addEntriesInOneColumn(entries, containerLayout);
        } else if (numCols > 1) {
            addEntriesInGrid(entries, containerLayout, numCols);
        } else {
            LogUtil.debug(TAG, "Number of columns less than 1");
        }
    }

    /**
     * API to set positions that were selected.
     *
     * @param position int value of position
     */
    public void setCheckedPosition(final int position) {
        setCheckedPositions(new ArrayList<Integer>() {{
            add(position);
        }});
    }

    /**
     * Sets list of data that should be populated.
     *
     * @param entries list of data
     */
    public void setEntries(List<String> entries) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        for (String entry : entries) {
            map.put(entry, entry);
        }
        this.entries = map;
        if (entries != null) {
            reDraw();
        }
    }

    /**
     * Sets list of data that should be populated.
     *
     * @param entries list of data in char format
     */
    public void setEntries(CharSequence[] entries) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        for (CharSequence entry : entries) {
            map.put(entry.toString(), entry.toString());
        }
        this.entries = map;
        if (entries != null) {
            reDraw();
        }
    }

    /**
     * Sets list of data that should be populated.
     *
     * @param entries list of data in hashmap format
     */
    public void setEntries(HashMap<String, String> entries) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.putAll(entries);
        this.entries = map;
        if (entries != null) {
            reDraw();
        }
    }

    /**
     * Sets list of data that should be populated.
     *
     * @param entries list of data in linkedhashmap type
     */
    public void setEntries(LinkedHashMap<String, String> entries) {
        this.entries = entries;
        if (entries != null) {
            reDraw();
        }
    }

    /**
     * Sets button click listener
     *
     * @param onButtonSelectedListener listener value
     */
    public void setOnButtonSelectedListener(OnButtonSelectedListener onButtonSelectedListener) {
        this.onButtonSelectedListener = onButtonSelectedListener;
    }

    private void addEntriesInOneColumn(HashMap<String, String> entries, DirectionalLayout containerLayout) {
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            FullWidthCompoundButton button = buildEntry(entry.getKey(), entry.getValue());
            containerLayout.addComponent(button);
            buttons.add(button);
        }
    }

    private void addEntriesInGrid(HashMap<String, String> entries, DirectionalLayout containerLayout, int numCols) {
        DirectionalLayout colContainer = null;

        List<String> keyList = new ArrayList<>(entries.keySet());

        for (int i = 0; i < entries.size(); i++) {
            if (i % numCols == 0) {
                colContainer = new DirectionalLayout(mContext);
                colContainer.setLayoutConfig(new DirectionalLayout.LayoutConfig(
                        DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT));
                colContainer.setOrientation(DirectionalLayout.HORIZONTAL);
                containerLayout.addComponent(colContainer);
            }

            String key = keyList.get(i);
            FullWidthCompoundButton button = buildEntry(key, entries.get(key));
            colContainer.addComponent(button);

            buttons.add(button);
        }

        // Ugly fix to force all cells to be equally distributed on parent's width
        for (int i = 1; i < keyList.size() % numCols; i++) {
            FullWidthCompoundButton hiddenBtn = buildEntry("hidden", "hidden");
            hiddenBtn.setVisibility(INVISIBLE);
            hiddenBtn.setClickable(false);
            colContainer.addComponent(hiddenBtn);
        }
    }

    private FullWidthCompoundButton buildEntry(String value, String label) {
        FullWidthCompoundButton fullWidthButton = new FullWidthCompoundButton(mContext);
        DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig(
                DirectionalLayout.LayoutConfig.MATCH_PARENT,
                DirectionalLayout.LayoutConfig.MATCH_PARENT);
        params.weight = (float) numCols;
        fullWidthButton.setLayoutConfig(params);
        fullWidthButton.setText(label);
        fullWidthButton.setValue(value);
        fullWidthButton.setCompoundType(compoundType);
        fullWidthButton.setLabelOrder(labelOrder);
        fullWidthButton.setListener(fullWidthCompoundButtonListener);
        return fullWidthButton;
    }

    private CompoundType getCompoundType(String compoundTypeInt) {
        switch (compoundTypeInt) {
            case "check_box":
                return CompoundType.CHECK_BOX;
            case "radio":
                return CompoundType.RADIO;
            default:
                LogUtil.error(TAG, "Unknown view Type");
                return CompoundType.CHECK_BOX;
        }
    }

    private LabelOrder getLabelOrder(String labelOrder) {
        switch (labelOrder) {
            case "before":
                return LabelOrder.BEFORE;
            case "after":
                return LabelOrder.AFTER;
            default:
                LogUtil.error(TAG,"Unrecognized label order");
                return LabelOrder.BEFORE;
        }
    }

    /**
     * Compound Type enum
     */
    public enum CompoundType {
        CHECK_BOX, RADIO
    }

    /**
     * Label order enum
     */
    public enum LabelOrder {
        BEFORE, AFTER
    }

    /**
     * Interface for click listener
     */
    public interface OnButtonSelectedListener {
        void onButtonSelected(int position, String value, boolean isChecked);
    }

    /**
     * Parse Style attributes
     */
    private class StyledAttributes {
        private final String entriesFlag = "entries";

        private final String numColsFlag = "numCols";

        private final String labelOrderFlag = "labelOrder";

        private final String compoundTypeFlag = "compoundType";

        private final String mEntries = "";
        private String mCompoundType = "check_box";
        private String mLabelOrder = "before";

        StyledAttributes(AttrSet attrs) {
            mCompoundType = attrs.getAttr(compoundTypeFlag).isPresent()
                    ? attrs.getAttr(compoundTypeFlag).get().getStringValue() : "check_box";
            compoundType = getCompoundType(mCompoundType);

            mLabelOrder = attrs.getAttr(labelOrderFlag).isPresent()
                    ? attrs.getAttr(labelOrderFlag).get().getStringValue()
                    : "before";
            labelOrder = getLabelOrder(mLabelOrder);

            numCols = attrs.getAttr(numColsFlag).isPresent() ? attrs.getAttr(numColsFlag)
                    .get()
                    .getIntegerValue() : 1;
        }
    }


    // *********************************************************
    // INNER CLASSES
    // *********************************************************

    // This inner class is needed to avoid that the onButtonClicked method (implemented from the
    // interface) is PUBLIC. Therefore it cannot be overrode.

    private class FullWidthCompoundButtonListener implements FullWidthCompoundButton.Listener {
        @Override
        public void onButtonClicked(Component component) {
            if (compoundType == CompoundType.RADIO) {
                for (FullWidthCompoundButton button : buttons) {
                    button.setChecked(false);
                }
            }

            if (onButtonSelectedListener != null) {
                FullWidthCompoundButton compoundButton = (FullWidthCompoundButton) component;
                boolean isChecked = !compoundButton.isChecked();
                int position = buttons.indexOf(component);
                String value = compoundButton.getValue();
                onButtonSelectedListener.onButtonSelected(position, value, isChecked);
            }
        }
    }
}
